package com.akka.demo.actor;

import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Terminated;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import com.akka.demo.di.SpringExtension;
import com.akka.demo.model.ReplyDeviceList;
import com.akka.demo.model.RequestDeviceList;
import com.akka.demo.model.TemperatureReading;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;


/**
 * Created by Administrator on 2018/2/26.
 * 设备组Actor
 */
@Component
@Scope("prototype")
public class DeviceGroup extends AbstractActor {

    static public class Temperature extends TemperatureReading{

        final private double value;

        public Temperature(double value) {
            this.value = value;
        }

        public double getValue() {
            return value;
        }
    }

    static public class TemperatureNotAvailable extends TemperatureReading{}

    static public class DeviceNotAvailable extends TemperatureReading{}

    static public class DeviceTimedOut extends TemperatureReading{}

    static public class RequestAllTemperatures{

        final private long requestId;

        public RequestAllTemperatures(long requestId) {
            this.requestId = requestId;
        }
    }

    static public class RespondAllTemperatures{
        final private long requestId;

        private Map<String, TemperatureReading> temperatures;

        public RespondAllTemperatures(long requestId, Map<String, TemperatureReading> temperatures) {
            this.requestId = requestId;
            this.temperatures = temperatures;
        }

        public long getRequestId() {
            return requestId;
        }

        public Map<String, TemperatureReading> getTemperatures() {
            return temperatures;
        }

        public void setTemperatures(Map<String, TemperatureReading> temperatures) {
            this.temperatures = temperatures;
        }
    }

    private LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this);

    final private String groupId;

    private Map<String, ActorRef> deviceIdToActor = new HashMap<>();

    private Map<ActorRef, String> actorToDeviceId = new HashMap<>();

    @Autowired
    private SpringExtension springExtension;

    public DeviceGroup(String groupId) {
        this.groupId = groupId;
    }

    @Override
    public void preStart() {
        log.info("DeviceGroup {} started", groupId);
    }

    @Override
    public void postStop() {
        log.info("DeviceGroup {} stopped", groupId);
    }

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(DeviceManager.RequestTrackDevice.class, trackMsg -> {
                    if (StringUtils.isEmpty(trackMsg.getGroupId()) || StringUtils.isEmpty(trackMsg.getDeviceId())) {
                        log.error("no groupId or deviceId in trackMsg!");
                    } else {
                        if (deviceIdToActor.containsKey(trackMsg.getDeviceId())) {
                            ActorRef deviceActor = deviceIdToActor.get(trackMsg.getDeviceId());
                            deviceActor.forward(trackMsg, context());
                        } else {
                            log.info("Creating device actor for {}", trackMsg.getDeviceId());
                            ActorRef deviceActor = context().actorOf(springExtension.props("device", trackMsg.getGroupId(), trackMsg.getDeviceId()), "device-" + trackMsg.getDeviceId());
                            context().watch(deviceActor);   //死亡观察功能
                            deviceIdToActor.put(trackMsg.getDeviceId(), deviceActor);
                            actorToDeviceId.put(deviceActor, trackMsg.getDeviceId());
                            deviceActor.forward(trackMsg, context());
                        }
                    }
                })
                .match(RequestDeviceList.class, requestDeviceList -> {
                    sender().tell(new ReplyDeviceList(requestDeviceList.getRequestId(), deviceIdToActor.keySet()), getSelf());
                })
                .match(DeviceManager.DeviceRegistered.class, deviceRegistered -> {
                    log.info("Device registered!!! --- from DeviceGroup");
                })
                .match(Terminated.class, terminated -> {  //当被观察的actor停止时，观察者会接收到Terminated消息
                    ActorRef actorRef = terminated.actor();
                    if (actorToDeviceId.containsKey(actorRef)) {
                        String deviceId = actorToDeviceId.get(actorRef);
                        log.info("Device actor for {} has been terminated", deviceId);
                        actorToDeviceId.remove(actorRef);
                        deviceIdToActor.remove(deviceId);
                    }
                })
                .build();
    }
}
